# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: xguo

import time

import dateutil.parser

import coin.exchange.base.kr_rest.level_book as level_book_mdl
import coin.exchange.base.kr_rest.public_client_base as pubcb
import coin.exchange.bitflyer_v1.kr_rest.native_public_client as npubc

from coin.exchange.base.kr_rest.rest_client_base import RestUpdate
from coin.proto.coin_market_query_pb2 import ProductTicker, ExchangeTicker
from coin.exchange.bitflyer_v1.kr_rest.futures_product import (
    BitflyerFuturesProduct,)
from coin.exchange.bitflyer_v1.kr_rest.constants import (
    active_symbol_list,)


class BitflyerFeedParser(object):
  @staticmethod
  def parse_native_ticker(update_msg, product):
    '''
    {
      "product_code": "BTC_JPY",
      "timestamp": "2018-03-01T03:12:12.317",
      "tick_id": 3752019,
      "best_bid": 1111567.0,
      "best_ask": 1112109.0,
      "best_bid_size": 0.008324,
      "best_ask_size": 0.007998,
      "total_bid_depth": 3082.40062116,
      "total_ask_depth": 3059.32290388,
      "ltp": 1112399.0,
      "volume": 223975.88527932,
      "volume_by_product": 21681.5709494
    }
    '''
    assert product.native_symbol == update_msg['product_code']
    # Bitflyer gives UTC time, but the time string format looks like
    # local time. Append 'Z' to the time string.
    timestamp = int(dateutil.parser.parse(update_msg['timestamp'] + 'Z').timestamp() * 1e+9)
    product_ticker = ProductTicker(
        last=float(update_msg['ltp']),  # Last trade price?
        bid=float(update_msg['best_bid']),
        bid_size=float(update_msg['best_bid_size']),
        ask=float(update_msg['best_ask']),
        ask_size=float(update_msg['best_ask_size']),
        exchange_timestamp=timestamp,
        # volume_24hr=  # need check
        symbol=product.symbol,
        native_symbol=product.native_symbol)
    return product_ticker

  @staticmethod
  def parse_exchange_ticker(update_msg):
    product_ticker_list = []
    for elem in update_msg:
      timestamp = int(dateutil.parser.parse(elem['timestamp'] + 'Z').timestamp() * 1e+9)
      product = BitflyerFuturesProduct.FromStr(elem['product_code'])
      ticker = elem['ticker']
      product_ticker = ProductTicker(last=float(ticker['LTP']),
                                     bid=float(ticker['best_bid']),
                                     bid_size=float(ticker['best_bid_size']),
                                     ask=float(ticker['best_ask']),
                                     ask_size=float(ticker['best_ask_size']),
                                     exchange_timestamp=timestamp,
                                     symbol=product.symbol,
                                     native_symbol=product.native_symbol)
      product_ticker_list.append(product_ticker)

    return ExchangeTicker(
        exchange="Bitflyer",
        each_ticker=product_ticker_list,
    )

  @staticmethod
  def parse_level_book(update_msg, product):
    level_book = level_book_mdl.LevelBook(product)

    for elem in update_msg['bids']:
      level_elem = level_book_mdl.LevelElement(float(elem['price']), float(elem['size']), 0)
      level_book.add_bid(level_elem)

    for elem in update_msg['asks']:
      level_elem = level_book_mdl.LevelElement(float(elem['price']), float(elem['size']), 0)
      level_book.add_ask(level_elem)

    level_book.finalize()
    return level_book


class BitflyerPublicClient(pubcb.PublicClientBase):
  def __init__(self, timeout=5, region='Japan'):
    self.npubc = npubc.BitflyerNativePublicClient(timeout=timeout, region=region)

  def query_ticker_impl(self, product):
    symbol = product.native_symbol
    update = self.npubc.get_ticker(symbol)
    update.msg = BitflyerFeedParser.parse_native_ticker(update.msg, product)
    return update

  def query_exchange_ticker_impl(self):
    update = self.npubc.get_exchange_ticker()
    update.msg = BitflyerFeedParser.parse_exchange_ticker(update.msg)
    return update

  def query_exchange_ticker_impl_obsolete(self):
    msg_list = []
    proto_list = []

    req_time = time.time()
    for native_symbol in active_symbol_list:
      product = BitflyerFuturesProduct.FromStr(native_symbol)
      json_msg = self.npubc.get_ticker(native_symbol).msg
      msg_list.append(json_msg)
      proto_list.append(BitflyerFeedParser.parse_native_ticker(json_msg, product))
    res_time = time.time()

    exchange_ticker = ExchangeTicker(
        exchange="Bitflyer",
        each_ticker=proto_list,
    )
    update = RestUpdate(req_time, res_time, exchange_ticker, native_msg=msg_list)
    return update

  def query_level_book_impl(self, product):
    symbol = product.native_symbol
    update = self.npubc.get_orderbook(symbol)
    update.msg = BitflyerFeedParser.parse_level_book(update.msg, product)
    return update
